package edu.pku.sei.transformation.testcase;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import edu.pku.sei.bridge.mdabase.adapter.ECoreLoader;
import edu.pku.sei.bridge.mdabase.adapter.EMFXMILoader;
import edu.pku.sei.mdabase.basictypes.PrimitiveType;
import edu.pku.sei.mdabase.infrastructure.metamodel.MetaClass;
import edu.pku.sei.mdabase.infrastructure.metamodel.MetaModelGraph;
import edu.pku.sei.mdabase.infrastructure.metamodel.MetaReference;
import edu.pku.sei.mdabase.infrastructure.model.MClass;
import edu.pku.sei.mdabase.infrastructure.model.MModelGraph;
import edu.pku.sei.mdabase.infrastructure.runtime.Context;
import edu.pku.sei.queryscript.parser.ScriptParser;
import edu.pku.sei.queryscript.parser.Ast.Ast;
import edu.pku.sei.transformation.modelType.ModelType;
import edu.pku.sei.transformation.pattern.TPatternEdge;
import edu.pku.sei.transformation.pattern.TPatternGraph;
import edu.pku.sei.transformation.pattern.TPatternNode;
import edu.pku.sei.transformation.semantics.TransformationController;
import edu.pku.sei.transformation.structure.Domain;
import edu.pku.sei.transformation.structure.Key;
import edu.pku.sei.transformation.structure.PatternDomain;
import edu.pku.sei.transformation.structure.Rule;
import edu.pku.sei.transformation.structure.RuleBasedEnvironment;
import edu.pku.sei.transformation.structure.Transformation;
import edu.pku.sei.transformation.structure.TypedModel;

public class PartialMatchTestCase
{
	
	private List<TypedModel> typedmodels = null;
	private Transformation trans = null;
	private RuleBasedEnvironment env = null;
	
	public List<TypedModel> getTypedModels()
	{
		if (typedmodels == null)
			typedmodels = new ArrayList<TypedModel>();
		return typedmodels;
	}

	
	public Transformation getTrans()
	{
		if (trans == null)
			trans = new Transformation();
		return trans;
	}
	
	public RuleBasedEnvironment getEnv()
	{
		if (env == null)
			env = new RuleBasedEnvironment();
		return env;
	}
	
	public static void main(String args[])
	{
		PartialMatchTestCase testcase = new PartialMatchTestCase();
		//testcase.createMetaModelGraph();
		ECoreLoader umlloader = ECoreLoader.getECoreLoader("SimpleUML", "UMLtoRDBMS\\metamodel\\SimpleUML.ecore");
		TypedModel sourceTypedModel = new TypedModel();
		ModelType sourceModelType = new ModelType();
		sourceModelType.setName("SimpleUML");
		sourceModelType.setMetamodel(umlloader.load());
		sourceTypedModel.setName("uml");
		sourceTypedModel.setType(sourceModelType);
		
		ECoreLoader rdbmsloader = ECoreLoader.getECoreLoader("SimpleRDBMS", "UMLtoRDBMS\\metamodel\\SimpleRDBMS.ecore");
		TypedModel targetTypedModel = new TypedModel();
		ModelType targetModelType = new ModelType();
		targetModelType.setName("SimpleRDBMS");
		targetModelType.setMetamodel(rdbmsloader.load());
		targetTypedModel.setName("rdbms");
		targetTypedModel.setType(targetModelType);
		
		testcase.getTypedModels().add(sourceTypedModel);
		testcase.getTypedModels().add(targetTypedModel);
		
//		for (MetaClass metaclass : sourceTypedModel.getType().getMetamodel().getNodes())
//		{
//			System.out.println(metaclass.toString());
//		}
//		for (MetaReference metaref : sourceTypedModel.getType().getMetamodel().getEdges())
//		{
//			System.out.println(metaref.toString());
//			System.out.println(metaref.getSource().toString());
//			System.out.println(metaref.getTarget().toString());
//		}
		
		EMFXMILoader srcloader = new EMFXMILoader("SimpleUML");
		MModelGraph sourceModel = srcloader.load("UMLtoRDBMS\\model\\SimpleUML.xmi");
		if (sourceModel == null)
			System.out.println("loading source model failed");
		sourceModel.setTypeGraph(sourceTypedModel.getType().getMetamodel());
		
		EMFXMILoader tarloader = new EMFXMILoader("SimpleRDBMS");
		MModelGraph targetModel = tarloader.load("UMLtoRDBMS\\SimpleRDBMS.xmi");
		if (targetModel == null)
			System.out.println("loading target model failed");
		targetModel.setTypeGraph(sourceTypedModel.getType().getMetamodel());
		
		testcase.createTranformationController();
		Transformation trans = testcase.getTrans();
		RuleBasedEnvironment env = testcase.getEnv();
		env.putCachedData(sourceModel.getManager(), "ModelManager", sourceModelType);
		env.putCachedData(targetModel.getManager(), "ModelManager", targetModelType);
		Context base = new Context();
		TransformationController.instance.registVariable(base, trans.getRule("ClassToTable"));
		base.setVariable("pn", "myPackage");
		base.setVariable("cn", "DVD");
		
		List<Domain> domains = new ArrayList<Domain>();
		for (PatternDomain domain : trans.getRule("ClassToTable").getDomains())
		{
			domains.add((Domain)domain);
		}

		System.out.println("Partial match result: \n");
		Context context_part = TransformationController.instance.partialMatch(domains.get(1), env, base);
		for (String str : context_part.getVariables().keySet())
		{
			System.out.println(str + ": " + context_part.getVariables().get(str).getValue().toString());
		}
		System.out.println("\n");
	}


	
	private void createTranformationController()
	{
		if (trans == null)
			trans = new Transformation();
		trans.setFormalParameters(typedmodels);
		createKeys();
		createRules();
		
	}
	
	private void createKeys()
	{
		Map<MetaClass, Key> keys = trans.getKeys();
		
		Key TableKey = new Key();
		TableKey.setTypeName("RdbmsTable");
		String[] TableKeyFactors = new String[2];
		TableKeyFactors[0] = "rdbmsName";
		TableKeyFactors[1] = "rdbmsSchema";
		TableKey.setFactors(TableKeyFactors);
		
		Key SchemaKey = new Key();
		SchemaKey.setTypeName("RdbmsSchema");
		String[] SchemaKeyFactors = new String[1];
		SchemaKeyFactors[0] = "rdbmsName";
		SchemaKey.setFactors(SchemaKeyFactors);
		
		Key ColumnKey = new Key();
		ColumnKey.setTypeName("RdbmsColumn");
		String[] ColumnKeyFactors = new String[1];
		ColumnKeyFactors[0] = "rdbmsName";
		ColumnKey.setFactors(ColumnKeyFactors);
		
		trans.addKey(typedmodels.get(1).getType().getMetamodel().getMetaClass("RdbmsTable"), TableKey);
		trans.addKey(typedmodels.get(1).getType().getMetamodel().getMetaClass("RdbmsSchema"), SchemaKey);
		trans.addKey(typedmodels.get(1).getType().getMetamodel().getMetaClass("RdbmsColumn"), SchemaKey);
	}
	
	private void createRules()
	{
		//What is a rule made of: name, variables, domains
		String exp;
		
		Rule class2table = new Rule();
		
		class2table.setName("ClassToTable");
		class2table.declareVariable("cn", PrimitiveType.TYPE_STRING, null);
		class2table.declareVariable("pn", PrimitiveType.TYPE_STRING, null);
		
		PatternDomain srcpd = new PatternDomain();
		TypedModel srctm = trans.getFormalParameter("uml");
		srcpd.setFormalParameter(srctm);
		TPatternGraph pattern_s = new TPatternGraph();
		
		//adding nodes and edges
		TPatternNode c = new TPatternNode();
		c.setName("c");
		c.setType(typedmodels.get(0).getType().getMetamodel().getMetaClass("UmlClass"));
		pattern_s.addNode(c);
		
		TPatternNode p = new TPatternNode();
		p.setName("p");
		p.setType(typedmodels.get(0).getType().getMetamodel().getMetaClass("UmlPackage"));
		pattern_s.addNode(p);
		
		TPatternEdge c2p = new TPatternEdge();
		MetaClass umlpackage = typedmodels.get(0).getType().getMetamodel().getMetaClass("UmlPackage");
		MetaClass umlpackageelement = typedmodels.get(0).getType().getMetamodel().getMetaClass("UmlPackageElement");
		MetaReference umlnamespace = typedmodels.get(0).getType().getMetamodel().getMetaReference("umlNamespace", umlpackageelement, umlpackage);
		c2p.setType(umlnamespace);
		c2p.setSource(c);
		c2p.setTarget(p);
		pattern_s.addEdge(c2p);
		
		srcpd.setPattern(pattern_s);
		
		exp = "c.umlName=cn;c.umlKind='Persistent';p.umlName=pn;";
		Ast ast2 = ScriptParser.parse(exp);
		srcpd.addExpression(ast2);
		class2table.addPatternDomain(srcpd);
		
		PatternDomain tarpd = new PatternDomain();
		TypedModel tartm = trans.getFormalParameter("rdbms");
		tarpd.setFormalParameter(tartm);
		TPatternGraph pattern_t = new TPatternGraph();
		
		TPatternNode t = new TPatternNode();
		t.setName("t");
		t.setType(typedmodels.get(1).getType().getMetamodel().getMetaClass("RdbmsTable"));
		pattern_t.addNode(t);
		
		TPatternNode s = new TPatternNode();
		s.setName("s");
		s.setType(typedmodels.get(1).getType().getMetamodel().getMetaClass("RdbmsSchema"));
		pattern_t.addNode(s);
		
		TPatternNode  cl = new TPatternNode();
		cl.setName("cl");
		cl.setType(typedmodels.get(1).getType().getMetamodel().getMetaClass("RdbmsColumn"));
		pattern_t.addNode(cl);
		
		TPatternNode  k = new TPatternNode();
		k.setName("k");
		k.setType(typedmodels.get(1).getType().getMetamodel().getMetaClass("RdbmsKey"));
		pattern_t.addNode(k);
		
		TPatternEdge t2s = new TPatternEdge();
		t2s.setType(typedmodels.get(1).getType().getMetamodel().getMetaReference("rdbmsSchema", t.getType(), s.getType()));
		t2s.setSource(t);
		t2s.setTarget(s);
		pattern_t.addEdge(t2s);
		
		TPatternEdge t2cl = new TPatternEdge();
		t2cl.setType(typedmodels.get(1).getType().getMetamodel().getMetaReference("rdbmsColumn", t.getType(), cl.getType()));
		t2cl.setSource(t);
		t2cl.setTarget(cl);
		pattern_t.addEdge(t2cl);
		
		TPatternEdge t2k = new TPatternEdge();
		t2k.setType(typedmodels.get(1).getType().getMetamodel().getMetaReference("rdbmsKey", t.getType(), k.getType()));
		t2k.setSource(t);
		t2k.setTarget(k);
		pattern_t.addEdge(t2k);
		
		TPatternEdge k2cl = new TPatternEdge();
		k2cl.setType(typedmodels.get(1).getType().getMetamodel().getMetaReference("rdbmsColumn", k.getType(), cl.getType()));
		k2cl.setSource(k);
		k2cl.setTarget(cl);
		pattern_t.addEdge(k2cl);
		
		tarpd.setPattern(pattern_t);
		
		exp = "t.rdbmsName=cn;s.rdbmsName=pn;";
		Ast ast3 = ScriptParser.parse(exp);
		tarpd.addExpression(ast3);
		exp = "cl.rdbmsName=cn+'_tid';cl.rdbmsType='NUMBER';";
		Ast ast4 = ScriptParser.parse(exp);
		tarpd.addExpression(ast4);
		exp = "k.rdbmsName=cn+'_pk';";
		Ast ast5 = ScriptParser.parse(exp);
		tarpd.addExpression(ast5);
		
		class2table.addPatternDomain(tarpd);
		
		trans.addRule(class2table);
		
	}
}